/*===============================================
Copyright (c): Technology Co.,Ltd. ALL rights reserved. 
                                                                 
  Create by: LIN
      Email:
       Date:
   Filename:
Description:
    Version:
Last Change:
                                                                 
=======================================*/
                                                                 
`ifndef SWITCH_REFM_SV
`define SWITCH_REFM_SV

class switch_refm extends uvm_component;
	
	bit [2:0] mac_lut[bit [47:0]];
	static int pkt_id = 0;

	uvm_blocking_get_port #(mac_seq_item) i_refm_port[PORT_NUM];
	uvm_analysis_port #(mac_seq_item) o_refm_ap[PORT_NUM];
	`uvm_component_utils(switch_refm)
	
	extern function new(string name, uvm_component parent);
	extern function void build_phase(uvm_phase phase);
	extern task main_phase(uvm_phase phase);
//	extern virtual task model();

endclass

function switch_refm::new(string name, uvm_component parent);
	super.new(name, parent);
endfunction

function void switch_refm::build_phase(uvm_phase phase);
	super.build_phase(phase);
	for (int i = 0; i < PORT_NUM; i++) begin
		i_refm_port[i] = new($sformatf("i_refm_port_%0d", i), this);
		o_refm_ap[i] = new($sformatf("o_refm_ap_%0d", i), this);
	end
endfunction

task switch_refm::main_phase(uvm_phase phase);
	
	mac_seq_item itrans[PORT_NUM];
	mac_seq_item otrans[PORT_NUM];
	for (int i = 0; i < PORT_NUM; i++)
	fork
		automatic int j = i;
		automatic bit [47:0] mac_addr;
		automatic bit [2:0] local_port;
		automatic int p = 0;
		while(1) begin
			i_refm_port[j].get(itrans[j]);
			pkt_id++;
	
			if (itrans[j].crc != itrans[j].calc_crc || itrans[j].dv_error == 1) begin
				`uvm_info("", "\n\033[33m*****************************************", UVM_MEDIUM);
				`uvm_info(get_type_name(), {"CRC ERROR or dv error. MAC Drop"}, UVM_MEDIUM)
				`uvm_info("", ($sformatf("[PKT_ID: %0d]", pkt_id)), UVM_MEDIUM);
				`uvm_info("", "\033[33m*****************************************\033[0m\n", UVM_MEDIUM);
				continue;
			end
			if ((itrans[j].pload.size < 46) || (itrans[j].pload.size > 1500)) begin
				`uvm_info("", "\n\033[33m****************************************", UVM_MEDIUM);
				`uvm_info(get_type_name(), {"PACKET SIZE ERROR. MAC Drop"}, UVM_MEDIUM)
				`uvm_info("", ($sformatf("[PKT_ID: %0d]", pkt_id)), UVM_MEDIUM);
				`uvm_info("", "\033[33m*****************************************\033[0m\n", UVM_MEDIUM);
				continue;
			end

			otrans[j] = new($sformatf("otrans_%0d", j));
			otrans[j].copy(itrans[j]);
			mac_addr = itrans[j].source_mac_addr;
			local_port = itrans[j].port_num;	
			if (!mac_lut.exists(mac_addr) || mac_lut[mac_addr] != local_port) begin
				mac_lut[mac_addr] = local_port;
			end
			mac_addr = itrans[j].destiny_mac_addr;
			otrans[j].pkt_id = pkt_id;
		//	otrans[j].input_time = $time;
			if (mac_addr == 48'hffffffffffff) begin
				`uvm_info("", "\033[33m*****************************************", UVM_MEDIUM);
				`uvm_info(get_type_name(), {"MAC Boardcast Packet"}, UVM_MEDIUM)
				`uvm_info("", $sformatf("[PKT_ID: %0d]", pkt_id), UVM_MEDIUM);
				`uvm_info("", "\033[33m*****************************************\033[0m\n", UVM_MEDIUM);

				for (p = 0; p < PORT_NUM; p++) begin
					if (p != local_port) begin
						o_refm_ap[p].write(otrans[j]);
					end
				end
			end
			else if (mac_lut.exists(mac_addr)) begin
				otrans[j].port_num = mac_lut[mac_addr];
				o_refm_ap[mac_lut[mac_addr]].write(otrans[j]);
				`uvm_info("", "\033[34m*****************************************", UVM_MEDIUM);
				`uvm_info(get_type_name(), {"LUT FIND. MAC Unicast"}, UVM_MEDIUM)
				`uvm_info("", $sformatf("[PKT_ID: %0d MAC: %12h -> PORT: %0d]", pkt_id, mac_addr, mac_lut[mac_addr]), UVM_MEDIUM);
				`uvm_info("", "\033[34m*****************************************\033[0m\n", UVM_MEDIUM);
			end
			else begin
				`uvm_info("", "\033[33m*****************************************", UVM_MEDIUM);
				`uvm_info(get_type_name(), {"LUT NOT FIND. MAC Boardcast"}, UVM_MEDIUM)
				`uvm_info("", $sformatf("[PKT_ID: %0d]", pkt_id), UVM_MEDIUM);
				`uvm_info("", "\033[33m*****************************************\033[0m\n", UVM_MEDIUM);
				for (p = 0; p < PORT_NUM; p++) begin
					if (p != local_port) begin
						o_refm_ap[p].write(otrans[j]);
					end
				end
			end


		end
	join_none

endtask

`endif
